
import { gameConfigHandle } from "core/config/gameConfig"
import Random from "core/utils/random"
import utils from "core/utils/utils"
import { FighterAttriModel, FighterModel } from "core/model/fighterModel"

import { HurtType, MapType, TeamID, TriggerType, UnitType } from "core/typeDefine/typeDefine"

import { FighterTeamInfoModel, TriggerEffectDataModel } from "core/model/battleInfoModel"

import { FighterSkillModel } from "core/model/skillModel"
import BattleHandle from "./battleHandle"
import fighterHandle from "core/handle/fighterHandle"

const BattlePoss = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const LessBattlePoss = [1, 2, 3, 6, 7, 8]
const NormalMapMonsterTypes = [
    [UnitType.Normal, UnitType.Normal],
    [UnitType.Elite, UnitType.Normal, UnitType.Normal],
    [UnitType.Elite, UnitType.Elite, UnitType.Normal],
    [UnitType.Lord, UnitType.Elite, UnitType.Normal],
    [UnitType.Lord, UnitType.Elite, UnitType.Elite, UnitType.Normal],
    [UnitType.Lord, UnitType.Lord, UnitType.Elite, UnitType.Elite],
    [UnitType.Lord, UnitType.Lord, UnitType.Elite, UnitType.Elite, UnitType.Elite],
    [UnitType.Lord, UnitType.Lord, UnitType.Elite, UnitType.Elite, UnitType.Elite],
    [UnitType.Lord, UnitType.Lord, UnitType.Elite, UnitType.Elite, UnitType.Elite],
    [UnitType.Lord, UnitType.Lord, UnitType.Elite, UnitType.Elite, UnitType.Elite],
    [UnitType.Lord, UnitType.Lord, UnitType.Elite, UnitType.Elite, UnitType.Elite],
    [UnitType.Lord, UnitType.Lord, UnitType.Elite, UnitType.Elite, UnitType.Elite],
]
class MonsterHandle {
    battleHandle: BattleHandle
    monsterList: number[] = []
    monsterList1: number[] = []
    monsterList2: number[] = []
    lordMonsterList: number[] = []
    bossMonsterList: number[] = []
    monsterQualityByUnitType: any = {
        [UnitType.Normal]: 0,
        [UnitType.Elite]: 1,
        [UnitType.Lord]: 2,
        [UnitType.Boss]: 3,
        [UnitType.WBoss]: 4,
    }
    constructor(battleHandle) {
        this.battleHandle = battleHandle
        this.initMonsterList()
    }
    initMonsterList() {
        let { battleHandle } = this
        let mapItemConfig = gameConfigHandle.configByID[battleHandle.mapID]
        // if (mapItemConfig.monsterListByFloor) {
        //     for (let i = 0; i < mapItemConfig.monsterListByFloor.length; i++) {
        //         let monsterLists = mapItemConfig.monsterListByFloor[i]
        //         if (battleHandle.floor >= monsterLists.floor) {
        //             monsterLists.list.forEach(monsterid => {
        //                 let monsterItemConfig = gameConfigHandle.configByID[monsterid]
        //                 if (monsterItemConfig.monsterType == UnitType.Boss) {
        //                     this.monsterList[UnitType.Boss].push(monsterid)
        //                 } else if (monsterItemConfig.monsterType == UnitType.Elite) {
        //                     this.monsterList[UnitType.Elite].push(monsterid)
        //                 } else if (monsterItemConfig.monsterType == UnitType.Lord) {
        //                     this.monsterList[UnitType.Lord].push(monsterid)
        //                 } else {
        //                     this.monsterList[UnitType.Normal].push(monsterid)
        //                 }
        //             })
        //         }
        //     }
        // } else 
        if (mapItemConfig.monsterList) {
            this.monsterList = mapItemConfig.monsterList.slice(0)
        }
        if (mapItemConfig.wbMonsterList) {
            this.monsterList = mapItemConfig.wbMonsterList.slice(0)
        }
        if (mapItemConfig.mapType == MapType.Normal) {
            let mapConfig = gameConfigHandle.gameConfig.map
            let monsterList1 = []
            let monsterList2 = []
            for (let i = 0; i < mapConfig.monsterListByIndex.length; i++) {
                let monsterLists = mapConfig.monsterListByIndex[i]
                if (mapItemConfig.monsterListIndex == monsterLists.index) {
                    monsterList2 = monsterLists.list.slice(0)
                    break
                } else if (monsterLists.index < mapItemConfig.monsterListIndex) {
                    monsterList1 = monsterList1.concat(monsterLists.list)
                }
            }
            this.monsterList1 = monsterList1
            this.monsterList2 = monsterList2
        }
        if (mapItemConfig.lordList) {
            this.lordMonsterList = mapItemConfig.lordList.slice(0)
        }
        if (mapItemConfig.bosses) {
            this.bossMonsterList = mapItemConfig.bosses.slice(0)
        }
        // else if (mapItemConfig.bossid) {
        //     this.monsterList[UnitType.Boss].push(mapItemConfig.bossid)
        // }
    }

    createMonsters(rand: Random) {
        let { battleHandle } = this
        let mapItemConfig = gameConfigHandle.configByID[battleHandle.mapID]
        let monsterAmount = utils.getIncValues(mapItemConfig.monsterAmount, battleHandle.stage)
        if (monsterAmount < 1) {
            monsterAmount = 1
        }
        if (monsterAmount > mapItemConfig.monsterMaxAmount) {
            monsterAmount = mapItemConfig.monsterMaxAmount
        }
        let monsterTeam: FighterModel[] = []
        let monsterTypes = NormalMapMonsterTypes[battleHandle.stage - 1].slice(0)
        let poss: number[]
        if (mapItemConfig.initMonsterBattlePoss && mapItemConfig.initMonsterBattlePoss[this.battleHandle.originFloor]) {
            poss = mapItemConfig.initMonsterBattlePoss[this.battleHandle.originFloor]
        } else {
            let emptyPoss = []
            if (monsterAmount <= 5) {
                emptyPoss = LessBattlePoss.slice(0)
            } else {
                emptyPoss = BattlePoss.slice(0)
            }
            poss = utils.getRandomArrayNoRepeat(emptyPoss, monsterAmount, battleHandle.rand)
        }
        if (this.battleHandle.mapType == MapType.WBoss) {
            for (let i = 0; i < poss.length && i < monsterAmount; i++) {
                let monster = this.createMonster(battleHandle.monsterLvl, UnitType.WBoss, rand)
                monsterTeam[poss[i]] = monster
            }
        } else {
            for (let i = 0; i < poss.length && i < monsterAmount; i++) {
                let monsterType = UnitType.Normal
                if (monsterTypes.length > 0) {
                    monsterType = utils.getOneFromArrayAndRemove(monsterTypes, rand)
                }
                let monster = this.createMonster(battleHandle.monsterLvl, monsterType, rand)
                monsterTeam[poss[i]] = monster
            }
            if (this.battleHandle.stage == this.battleHandle.maxStage && this.battleHandle.maxStage >= 3) {
                let pos = utils.getOneFromArray([2, 7], rand)
                let monster = this.createMonster(battleHandle.monsterLvl + battleHandle.stage - 1, UnitType.Boss, rand)
                monsterTeam[pos] = monster
            }
        }

        let fighterTeamInfo = new FighterTeamInfoModel()
        fighterTeamInfo.team = monsterTeam

        return fighterTeamInfo
    }

    createBoss(floor: number, monsterAmountMax: number, rand: Random) {
        let monster = this.createMonster(floor, UnitType.Boss, rand)
        return monster
    }

    createMonster(monsterLvl: number, monsterType: UnitType, rand: Random) {

        let { battleHandle } = this
        let mapConfig = gameConfigHandle.gameConfig.map
        let monsterid = null
        let mapItemConfig = gameConfigHandle.configByID[battleHandle.mapID]
        let monsterItemConfig = gameConfigHandle.configByID[monsterid]
        if (battleHandle.mapType == MapType.Abyss) {
            monsterid = utils.getOneFromArray(this.monsterList, rand)
        } else if (battleHandle.mapType == MapType.WBoss) {
            monsterid = this.monsterList[this.battleHandle.weedDay]
        } else {
            if (monsterType == UnitType.Boss) {
                monsterid = utils.getOneFromArray(this.bossMonsterList, rand)
            } else if (monsterType == UnitType.Lord) {
                monsterid = utils.getOneFromArray(this.lordMonsterList, rand)
            } else {
                if (mapItemConfig.mapType == MapType.Normal) {
                    if (this.monsterList1.length > 0 && rand.intn(100) < 25) {
                        monsterid = utils.getOneFromArray(this.monsterList1, rand)
                    } else {
                        monsterid = utils.getOneFromArray(this.monsterList2, rand)
                    }
                } else {
                    monsterid = utils.getOneFromArray(this.monsterList, rand)
                }

            }
            if (!monsterid) {
                monsterType = UnitType.Normal
                if (mapItemConfig.mapType == MapType.Normal) {
                    if (this.monsterList1.length > 0 && rand.intn(100) < 25) {
                        monsterid = utils.getOneFromArray(this.monsterList1, rand)
                    } else {
                        monsterid = utils.getOneFromArray(this.monsterList2, rand)
                    }
                } else {
                    monsterid = utils.getOneFromArray(this.monsterList, rand)
                }
            }

        }

        monsterItemConfig = gameConfigHandle.configByID[monsterid]
        let fighter = new FighterModel()
        fighter.tid = monsterid
        fighter.lvl = monsterLvl
        fighter.unitType = monsterType
        if (battleHandle.mapType == MapType.Abyss || battleHandle.mapType == MapType.Front) {
            fighter.quality = monsterItemConfig.quality
            if (fighter.quality >= 2) {
                fighter.quality = 3
            }
        } else {
            fighter.quality = this.monsterQualityByUnitType[fighter.unitType]

        }
        let monsterAttriCoe = this.getMonsterAttriCoe(monsterLvl)
        let monsterConfig = gameConfigHandle.gameConfig.monster
        let monsterTypeAdd = monsterConfig.monsterTypeAdd[monsterType]
        let monsterTypeHpAdd = monsterConfig.monsterTypeHpAdd[monsterType]
        let monsterRand = new Random(monsterid)
        utils.entries(monsterConfig.baseAttri).forEach(attris => {
            let attriName = attris[0]
            let attriValue = attris[1]
            let value = utils.getIncValues(attriValue, monsterLvl)
            if (attriName == "maxHp") {
                value = value * monsterTypeHpAdd * monsterAttriCoe
            } else {
                let add = monsterTypeAdd
                if (monsterType == UnitType.WBoss && (attriName == "critRate" || attriName == "critHurtPer")) {
                    add /= 15
                }
                value = value * add * monsterAttriCoe
            }
            value = Math.floor(value * (100 + monsterRand.getRandomNumber(1, 35)) / 100)
            fighter.attris[attriName] += value
        })
        if (fighter.quality >= 2 && mapItemConfig.mapType !== MapType.Normal) {
            utils.entries(monsterConfig.otherAttris).forEach(attris => {
                let attriName = attris[0]
                let attriValueConfig = attris[1]
                let value = utils.getIncValues(attriValueConfig.lvlValues, monsterLvl)
                if (value > attriValueConfig.lvlMaxValue) {
                    value = attriValueConfig.lvlMaxValue
                }
                value = utils.floor(value * monsterRand.getRandomNumber(30, 100) / 100, 1)
                fighter.attris[attriName] += value
            })
        }


        if (monsterItemConfig) {
            if (monsterItemConfig.skills) {
                monsterItemConfig.skills.forEach((skillid, index) => {
                    if (this.battleHandle.mapType !== MapType.Normal || (this.battleHandle.mapType == MapType.Normal && index <= 1)) {
                        let skill = new FighterSkillModel()
                        skill.tid = skillid
                        skill.lvl = 1
                        fighter.skills.push(skill)
                    }

                })
            }
        }
        //世界boss 额外技能
        // if (this.battleHandle.mapID == 300202) {
        //     gameConfigHandle.configByID[300202].enemySkills.forEach(id => {
        //         let skill = new FighterSkillModel()
        //         skill.tid = id
        //         skill.lvl = 1
        //         fighter.skills.push(skill)
        //     })
        // }

        fighterHandle.calcSkillsAttris(fighter)
        fighterHandle.calcBattleFighterAttri(fighter)
        fighter.battleAttris.hp = fighter.battleAttris.maxHp

        return fighter
    }

    getMonsterAttriCoe(monsterLvl: number) {
        let monsterConfig = gameConfigHandle.gameConfig.monster
        for (let i = 0; i < monsterConfig.monsterLvlAttriCoe.length; i++) {
            let monsterLvlAttriCoe = monsterConfig.monsterLvlAttriCoe[i]
            if (monsterLvl >= monsterLvlAttriCoe.lvl[0] && monsterLvl <= monsterLvlAttriCoe.lvl[1]) {
                return monsterLvlAttriCoe.attriCoe
            }
        }
    }

    monsterLvlUp(fighter: FighterModel, rand: Random) {
        let mapConfig = gameConfigHandle.gameConfig.map
        let monsterItemConfig = gameConfigHandle.configByID[fighter.tid]
        let lvluptid = monsterItemConfig.lvluptid
        let lvlupMonsterItemConfig = gameConfigHandle.configByID[lvluptid]
        fighter.tid = lvluptid
        let lvl = fighter.lvl
        let hp = fighter.battleAttris.hp
        let hpPer = fighter.battleAttris.hp / fighter.battleAttris.maxHp
        let monsterType = fighter.unitType
        if (monsterType == UnitType.Init) {
            monsterType = UnitType.Normal
        } else if (monsterType == UnitType.Normal) {
            monsterType = UnitType.Elite
        } else if (monsterType == UnitType.Elite) {
            monsterType = UnitType.Lord
        }
        fighter.unitType = monsterType
        let monsterConfig = gameConfigHandle.gameConfig.monster
        let monsterTypeAdd = monsterConfig.monsterTypeAdd[monsterType]
        let monsterTypeHpAdd = monsterConfig.monsterTypeHpAdd[monsterType]
        fighter.attris = new FighterAttriModel()
        utils.entries(monsterConfig.baseAttri).forEach(attris => {
            let attriName = attris[0]
            let attriValue = attris[1]
            let value = utils.getIncValues(attriValue, lvl)
            if (attriName == "maxHp") {
                value = value * monsterTypeHpAdd
            } else {
                value = value * monsterTypeAdd
            }

            fighter.attris[attriName] += value
        })
        if (lvlupMonsterItemConfig && lvlupMonsterItemConfig.skillid) {
            let skill = new FighterSkillModel()
            skill.tid = lvlupMonsterItemConfig.skillid
            skill.lvl = 1
            fighter.skills.push(skill)
        }
        fighter.monsterIndex = monsterItemConfig.monsterTypeIndex
        fighterHandle.calcSkillsAttris(fighter)
        fighterHandle.calcBattleFighterAttri(fighter)
        fighter.battleAttris.hp = Math.ceil(fighter.battleAttris.maxHp * hpPer)
        if (fighter.battleAttris.hp > fighter.battleAttris.maxHp) {
            fighter.battleAttris.hp = fighter.battleAttris.maxHp
        }
        return fighter

    }

    addBoss() {
        let { battleHandle } = this
        let mapItemConfig = gameConfigHandle.configByID[battleHandle.mapID]
        if (mapItemConfig.isBoss == false) {
            return
        }
        battleHandle.bossAddMonsterCd = utils.getIncValues(mapItemConfig.bossAddMonsterCd, battleHandle.floor)
        if (battleHandle.bossAddMonsterCd < mapItemConfig.bossAddMonsterMinCd) {
            battleHandle.bossAddMonsterCd = mapItemConfig.bossAddMonsterMinCd
        }
        battleHandle.bossX = utils.getRandomNumber(2, battleHandle.maxCol - 3, battleHandle.rand)
        battleHandle.bossY = utils.getRandomNumber(2, battleHandle.maxRow - 3, battleHandle.rand)
        for (let x = battleHandle.bossX - 1; x <= battleHandle.bossX + 1; x++) {
            for (let y = battleHandle.bossY - 1; y <= battleHandle.bossY + 1; y++) {
                let pos = battleHandle.getBattlePos(x, y)
                if (x == battleHandle.bossX && y == battleHandle.bossY) {
                    battleHandle.bossPoss[pos] = 2
                } else {
                    battleHandle.bossPoss[pos] = 1
                }
            }
        }
        let monster = battleHandle.monsterHandle.createBoss(battleHandle.floor, battleHandle.monsterAmountMax, battleHandle.rand)
        monster.teamIndexID = battleHandle.teamIndexID++
        monster.teamID = TeamID.Team2
        monster.battlePos = battleHandle.getBattlePos(battleHandle.bossX, battleHandle.bossY)
        battleHandle.team2[monster.battlePos] = monster
        battleHandle.monsters[monster.teamIndexID] = monster
        battleHandle.boss = monster
        battleHandle.monsterAliveAmount += 1
        battleHandle.monsterAmount += 1
        battleHandle.fighterStart(monster)
        battleHandle.battleUpdate(0)
        battleHandle.battleUpdate(3, monster)
        battleHandle.timerHandle.setInterval(() => {
            if (battleHandle.boss.battleAttris.hp >= 1) {
                this.addMonster(null, battleHandle.boss.teamIndexID)
            }
        }, battleHandle.bossAddMonsterCd)
    }

    async addMonster(addAmount, bossid: number) {
        let { battleHandle } = this
        if (!bossid) {
            if (battleHandle.monsterAmount >= battleHandle.monsterAmountMax) {
                return
            }
        }
        if (!addAmount) {
            let minAmount = Math.floor(battleHandle.floor / 100)
            if (minAmount > 4) {
                minAmount = 4
            } else if (minAmount < 1) {
                minAmount = 1
            }
            let maxAmount = Math.floor(battleHandle.floor / 50)
            if (maxAmount > 10) {
                maxAmount = 10
            } else if (maxAmount < 1) {
                maxAmount = 1
            }
            addAmount = utils.getRandomNumber(minAmount, maxAmount, battleHandle.rand)
            if (!bossid) {
                if (battleHandle.monsterAmount + addAmount >= battleHandle.monsterAmountMax) {
                    addAmount = battleHandle.monsterAmountMax - battleHandle.monsterAmount
                }
            }
        }

        let emptyPoss = []
        for (let pos = 0; pos < battleHandle.maxCol * battleHandle.maxRow; pos++) {
            if (!battleHandle.bossPoss[pos]) {
                if (!battleHandle.team2[pos] || battleHandle.team2[pos].battleAttris.hp < 1) {
                    emptyPoss.push(pos)
                }
            }
        }
        if (emptyPoss.length == 0) {
            return
        }

        let poss = utils.getRandomArrayNoRepeat(emptyPoss, addAmount, battleHandle.rand)
        let monsters: FighterModel[] = []
        for (let i = 0; i < poss.length; i++) {
            let pos = poss[i]
            let monsterType = UnitType.Normal
            let randValue = battleHandle.rand.intn(100)
            if (randValue < 40) {
                monsterType = UnitType.Elite
            }
            let floor = battleHandle.floor
            if (battleHandle.mapType == MapType.Instance) {
                floor += battleHandle.monsterAmount
            }

            let monster = battleHandle.monsterHandle.createMonster(floor, monsterType, battleHandle.rand)
            monster.teamIndexID = battleHandle.teamIndexID++
            monster.teamID = TeamID.Team2
            monster.battlePos = pos
            monster.bossTeamIndexID = bossid
            battleHandle.monsterAmount += 1
            battleHandle.monsterAliveAmount += 1
            if (bossid) {
                battleHandle.monsterAmountMax += 1
            }
            monsters.push(monster)
            battleHandle.battleUpdate(3, monster)
            await battleHandle.battleSkillHandle.triggerEffects(monster, TriggerType.CreateMonster, monster, new TriggerEffectDataModel({}))
        }
        await battleHandle.battleUpdate(0)
        if (bossid) {
            battleHandle.addMonsterAnimation(battleHandle.boss.teamIndexID)
        }
        if (monsters.length > 0) {
            monsters.forEach(monster => {
                battleHandle.monsters[monster.teamIndexID] = monster
                battleHandle.team2[monster.battlePos] = monster
                battleHandle.fighterStart(monster)
            })
        }
    }


}




export default MonsterHandle
